package com.wolfking.jeesite.mybatis;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.wolfking.jeesite.entity.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.reflections.ReflectionUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 基类的service
 *
 * @author wolfking@赵伟伟
 * @mail zww199009@163.com
 * @创作日期 2017年4月26日 下午8:19:03
 * @copyright wolfking
 */
@Slf4j
public abstract class BaseService<T>
        extends ServiceImpl<BaseMapper<T>, T>
        implements InitializingBean {

    @Autowired
    @SuppressWarnings("all")
    protected BaseMapper<T> mapper;
    // 具体操作的实体类
    private Class<T> clazz;
    // 主键的字段
    private Field idField;

    private final Map<Field, String> fieldStringMap = Maps.newHashMap();

    /**
     * 添加
     *
     * @param t
     * @return
     */
    @Transactional
    public boolean save(T t) {
        if (t instanceof ParentEntity) {
            ParentEntity current = (ParentEntity) t;
            ParentEntity parent = ParentEntity.class.cast(mapper.selectById(current.getParentId()));
            if (parent != null) {
                if (parent.getParentIds().endsWith(","))
                    current.setParentIds(parent.getParentIds() + parent.getId() + ",");
                else
                    current.setParentIds(parent.getParentIds() + "," + parent.getId() + ",");
            }
        }
        return super.save(t);
    }

    /**
     * 更新
     *
     * @param t
     * @return
     */
    @Transactional
    public boolean updateById(T t) {
        if (t instanceof ParentEntity) {
            T old = mapper.selectById(((ParentEntity) t).getId());
            ParentEntity oldEntity = (ParentEntity) old;
            ParentEntity currentEntity = ParentEntity.class.cast(t);
            ParentEntity parentEntity = ParentEntity.class.cast(mapper.selectById(currentEntity.getParentId()));
            if (parentEntity != null) {
                if (parentEntity.getParentIds().endsWith(","))
                    currentEntity.setParentIds(parentEntity.getParentIds() + parentEntity.getId() + ",");
                else
                    currentEntity.setParentIds(parentEntity.getParentIds() + "," + parentEntity.getId() + ",");
            }
            mapper.updateById(t);
            if (parentEntity != null && !oldEntity.getParentId().equals(currentEntity.getParentId())) {
                String parentIds = ParentEntity.class.cast(old).getParentIds();
                List<T> list = mapper.selectList(new QueryWrapper<T>().likeRight("parent_ids", parentIds));
                for (T tt : list) {
                    ParentEntity entity = (ParentEntity) tt;
                    entity.setParentIds(entity.getParentIds().replace(
                            oldEntity.getParentIds() + oldEntity.getId() + ","
                            , currentEntity.getParentIds() + currentEntity.getId() + ","));
                    mapper.updateById((T) entity);
                }
                log.info("updateParentIds size is {} ", list.size());
            } else {
                log.info("parent id equal not update updateParentIds");
            }
        } else
            mapper.updateById(t);
        return true;
    }

    /**
     * 根据ID删除
     *
     * @param id
     * @return
     */
    @Transactional
    public List<T> deleteById(Serializable id) {
        T t = mapper.selectById(id);
        List<T> list = Lists.newArrayList();
        if (t != null) {
            list.add(t);
            if (t instanceof ParentEntity) {
                String parentIds = ParentEntity.class.cast(t).getParentIds();
                if (StringUtils.isNotBlank(parentIds)) {
                    QueryWrapper<T> queryWrapper = new QueryWrapper<>();
                    queryWrapper.likeRight("parent_ids", parentIds + ((ParentEntity) t).getId() + ",");
                    list.addAll(mapper.selectList(queryWrapper));
                }
            }
        }
        List<Serializable> idList = Lists.newArrayList();
        for (T e : list) {
            idList.add(getId(e));
        }
        if (idList.size() > 0) {
            mapper.deleteBatchIds(idList);
        }
        return list;
    }

    /**
     * 模糊匹配查询
     *
     * @param t
     * @return
     */
    @Transactional(readOnly = true)
    public List<T> selectLike(T t) {
        return mapper.selectList(queryWrapperVague(t));
    }

    /**
     * 精确匹配查询
     *
     * @param t
     * @return
     */
    @Transactional(readOnly = true)
    public List<T> selectExample(T t) {
        return mapper.selectList(queryWrapperAccuracy(t));
    }

    public QueryWrapper<T> queryWrapperAccuracy(T t) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        fieldStringMap.forEach((f, c) -> {
            try {
                final Object o = f.get(t);
                if (o != null) {
                    queryWrapper.eq(c, o);
                }
            } catch (Exception e) {
                log.warn("", e);
            }
        });
        return queryWrapper;
    }

    public QueryWrapper<T> queryWrapperVague(T t) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        fieldStringMap.forEach((f, c) -> {
            try {
                final Object o = f.get(t);
                if (o instanceof String && StringUtils.isNotBlank(o.toString())) {
                    queryWrapper.like(c, o.toString().trim());
                }
                if (o != null && !(o instanceof String)) {
                    queryWrapper.eq(c, o);
                }
            } catch (Exception e) {
                log.warn("", e);
            }
        });
        return queryWrapper;
    }

    public QueryWrapper<T> queryWrapperConfig(T t, Set<String> accuracyField, Set<String> vagueField) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        fieldStringMap.forEach((f, c) -> {
            try {
                final Object o = f.get(t);
                if (o != null && (accuracyField.contains(f.getName()) || accuracyField.contains(c))
                        && (!(o instanceof String) || StringUtils.isNotBlank(o.toString()))) {
                    queryWrapper.eq(c, o);
                }
                if (o instanceof String && StringUtils.isNotBlank(o.toString())
                        && (vagueField.contains(f.getName()) || vagueField.contains(c))) {
                    queryWrapper.like(c, o.toString().trim());
                }

            } catch (Exception e) {
                log.warn("", e);
            }
        });
        return queryWrapper;
    }

    /**
     * 查询总数
     *
     * @return
     */
    @Transactional(readOnly = true)
    public long countAll() {
        return mapper.selectCount(new QueryWrapper<>());
    }

    /**
     * 模糊匹配,查询总数
     *
     * @param t
     * @return
     */
    @Transactional(readOnly = true)
    public long countLike(T t) {
        return mapper.selectCount(queryWrapperVague(t));
    }

    /**
     * 精确匹配,查询总数
     *
     * @param t
     * @return
     */
    @Transactional(readOnly = true)
    public long countExample(T t) {
        return mapper.selectCount(queryWrapperAccuracy(t));
    }

    /**
     * 模糊匹配删除实体
     *
     * @return
     */
    @Transactional
    public long deleteLike(T t) {
        return mapper.delete(queryWrapperVague(t));
    }

    /**
     * 精确匹配删除实体
     *
     * @return
     */
    @Transactional
    public int deleteExample(T t) {
        return mapper.delete(queryWrapperAccuracy(t));
    }

    /**
     * 模糊匹配分页查询
     *
     * @param t
     * @param pageNum
     * @param pageSize
     * @return
     */
    @Transactional(readOnly = true)
    public PageInfo<T> pageLike(T t, int pageNum, int pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        List<T> list = mapper.selectList(queryWrapperVague(t));
        return new PageInfo<>(list);
    }

    /**
     * 精确匹配分页查询
     *
     * @param t
     * @param pageNum
     * @param pageSize
     * @return
     */
    @Transactional(readOnly = true)
    public PageInfo<T> pageExample(T t, int pageNum, int pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        List<T> list = mapper.selectList(queryWrapperAccuracy(t));
        return new PageInfo<>(list);
    }


    @Transactional(readOnly = true)
    public PageInfo<T> pageQuery(Wrapper<T> queryWrapper, int pageNum, int pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        List<T> list = this.list(queryWrapper);
        return new PageInfo<>(list);
    }

    @SuppressWarnings("unchecked")
    private Serializable getId(Object obj) {
        try {
            return (Serializable) idField.get(obj);
        } catch (Exception e) {
            return null;
        }
    }

    @Transactional(readOnly = true)
    public List<T> getAllChild(Integer parentId) {
        T entity = null;
        try {
            entity = (T) clazz.newInstance();
        } catch (Exception e) {
            log.warn("", e);
        }
        if (entity instanceof ParentEntity) {
            T t = getById(parentId);
            ParentEntity parent = ParentEntity.class.cast(t);
            if (parent == null)
                return list();
            String parentIds = parent.getParentIds() + (parent.getParentIds().endsWith(",") ? "" : ",")
                    + parent.getId();
            QueryWrapper<T> queryWrapper = new QueryWrapper<>();
            queryWrapper.like("parent_ids", parentIds);
            List<T> list = mapper.selectList(queryWrapper);
            list.add(getById(parentId));
            return list;
        } else
            return Lists.newArrayList();
    }

    /*
     * （非 Javadoc）
     *
     * @see
     * org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
     */
    @Override
    @SuppressWarnings("unchecked")
    public void afterPropertiesSet() throws Exception {
        Type type = this.getClass().getGenericSuperclass();
        ParameterizedType pt = (ParameterizedType) type;
        clazz = (Class<T>) pt.getActualTypeArguments()[0];
        TableInfo tableInfo = TableInfoHelper.getTableInfo(clazz);
        List<TableFieldInfo> fieldList = tableInfo.getFieldList();
        try {
            idField = ReflectionUtils.getAllFields(
                    clazz, ReflectionUtils.withName(tableInfo.getKeyProperty())).iterator().next();
            idField.setAccessible(true);
        } catch (Exception e) {
            idField = null;
            log.error("[{}]->{} primary field not exists", clazz.getName(), tableInfo.getKeyProperty());
        }
        for (TableFieldInfo tableFieldInfo : fieldList) {
            try {
                Field field = ReflectionUtils.getAllFields(
                        clazz, ReflectionUtils.withName(tableFieldInfo.getProperty())).iterator().next();
                field.setAccessible(true);
                fieldStringMap.put(field, tableFieldInfo.getColumn());
            } catch (Exception e) {
                log.error("[{}]->{} field not exists", clazz.getName(), tableFieldInfo.getProperty());
            }
        }
    }
}
